﻿#region Header

/* 
 * Copyright (c) EFACEC Sistemas de Electrónica, S.A.
 * 
 * EFACEC Sistemas de Electrónica, S.A
 * Rua Engº Frederico Ulrich
 * Apart. 3078 - 4471-907 Moreira Maia
 * PORTUGAL 
 * Tel: +351 22 940 2000
 * Fax: +351 22 948 5428
 * 
 * All rights reserved. This source code contains confidential, trade secret 
 * material of EFACEC Sistemas de Electrónica, S.A. Any attempt or 
 * participation in distributing, deciphering, decoding, reverse engineering 
 * or in any way using or altering the source code is strictly prohibited 
 * without prior consent of EFACEC Sistemas de Electrónica, S.A.
 * 
 */

#endregion

#region Using Directives

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.ComponentModel;

#endregion

namespace VFDDriver
{

    /// <summary> 
    /// Base class that should be used by all classes that need to create and manage threads.
    /// Implements the default behaviour to create and control a thread, sets its priority, and gets its status.
    /// </summary>
    public abstract class Task
    {
        /// <summary>
        /// Task's possible states.
        /// </summary>
        public enum TaskState
        {
            /// <summary>
            /// Task is currently stopped.
            /// </summary>
            Stopped,
            /// <summary>
            /// Task is working.
            /// </summary>
            Working,
            /// <summary>
            /// Task is finishing is work.
            /// </summary>
            Finishing,
            /// <summary>
            /// Task is beeing aborted.
            /// </summary>
            Aborting,
            /// <summary>
            /// Task is terminating all internal processes.
            /// </summary>
            Terminating,
            /// <summary>
            /// Task is handling an exception that occurred.
            /// </summary>
            HandlingException
        }

        #region Properties

        private volatile object _syncRoot;
        /// <summary>Gets an object that can be used to synchronize access to the object.</summary>
        public object SyncRoot
        {
            get { return _syncRoot; }
        }

        #region Base thread properties


        private Thread _baseThread;

        /// <summary>Gets an indication if the thread is running.</summary>
        public virtual bool IsAlive
        {
            get
            {
                lock (this.SyncRoot)
                    return _baseThread.IsAlive;
            }
        }

        /// <summary>Gets or sets the name of the thread.</summary>
        /// <exception cref="System.InvalidOperationException">
        /// A set operation was requested, and the Name property has already been set.
        /// </exception>
        public virtual string Name
        {
            get
            {
                lock (this.SyncRoot)
                    return _baseThread.Name;
            }
            set
            {
                lock (this.SyncRoot)
                    _baseThread.Name = value;
            }
        }

        /// <summary>
        /// Gets an indication if the main thread should stop.
        /// </summary>
        protected volatile bool _ShouldStop;

        /// <summary>Gets an indication if the main thread should stop.</summary>
        public bool ShouldStop
        {
            get { return _ShouldStop; }
        }

        /// <summary>
        /// Gets an indication if the main thread should restart if an error occurred.
        /// </summary>
        protected bool _shouldRestart;

        /// <summary>Gets an indication if the main thread should restart if an error occurred.</summary>
        public bool ShouldRestart
        {
            get { return _shouldRestart; }
        }


        /// <summary>Gets or sets a value indicating the scheduling priority of the base thread.</summary>
        /// <exception cref="System.ArgumentException">The value specified for a set operation is not a valid ThreadPriority value.</exception>
        /// <exception cref="System.Threading.ThreadStateException">The thread has reached a final state, such as System.Threading.ThreadState.Aborted.</exception>
        public virtual ThreadPriority Priority
        {
            get
            {
                lock (this.SyncRoot)
                    return _baseThread.Priority;
            }
            set
            {
                lock (this.SyncRoot)
                    _baseThread.Priority = value;
            }
        }

        /// <summary>Gets a value containing the states of the current base thread.</summary>
        public virtual ThreadState ThreadState
        {
            get
            {
                lock (this.SyncRoot)
                    return _baseThread.ThreadState;
            }
        }

        #endregion

        private volatile TaskState _state;
        /// <summary>Gets a value containing the state of the current task.</summary>
        public TaskState State
        {
            get
            {
                lock (this.SyncRoot)
                    return _state;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the Task class.
        /// </summary>
        public Task()
        {
            _baseThread = new Thread(new ThreadStart(Worker));
            _state = TaskState.Stopped;
            _syncRoot = new object();
            _ShouldStop = false;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Causes the operating system to change the state of the current instance to
        /// System.Threading.ThreadState.Running.
        /// </summary>
        /// <exception cref="System.Threading.ThreadStateException">
        /// The thread has already been started.
        /// </exception>
        /// <exception cref="System.Security.SecurityException">
        /// The caller does not have the appropriate System.Security.Permissions.SecurityPermission.
        /// </exception>
        /// <exception cref="System.OutOfMemoryException">
        /// There is not enough memory available to start this thread.
        /// </exception>
        public virtual void Start()
        {
            if (_baseThread.ThreadState == ThreadState.Unstarted)
                _baseThread.Start();
        }
        public virtual void Start(long watchDogTimeout)
        {
            if (_baseThread.ThreadState == ThreadState.Unstarted)
            {
                _baseThread.Start();
            }
        }

        ///// <summary>
        ///// Causes the operating system to change the state of the current instance to
        ///// System.Threading.ThreadState.Running, and optionally supplies an object containing
        ///// data to be used by the method the thread executes.
        ///// </summary>
        ///// <param name="parameter">
        ///// An object that contains data to be used by the method the thread executes.
        ///// </param>
        ///// <exception cref="System.Threading.ThreadStateException">
        ///// The thread has already been started.
        ///// </exception>
        ///// <exception cref="System.Security.SecurityException">
        ///// The caller does not have the appropriate System.Security.Permissions.SecurityPermission.
        ///// </exception>
        ///// <exception cref="System.OutOfMemoryException">
        ///// There is not enough memory available to start this thread.
        ///// </exception>
        ///// <exception cref="System.InvalidOperationException">
        ///// This thread was created using a System.Threading.ThreadStart delegate instead
        ///// of a System.Threading.ParameterizedThreadStart delegate.
        ///// </exception>
        //public virtual void Start(object parameter)
        //{
        //    _baseThread.Start(parameter);
        //}

        /// <summary>
        /// Raises a System.Threading.ThreadAbortException in the thread on which it
        /// is invoked, to begin the process of terminating the thread. Calling this
        /// method usually terminates the thread.
        /// </summary>
        /// <exception cref="System.Security.SecurityException">The caller does not have the required permission.</exception>
        /// <exception cref="System.Threading.ThreadStateException">The thread that is being aborted is currently suspended.</exception>
        public virtual void Abort()
        {
            _baseThread.Abort();
        }

        /// <summary>
        /// Blocks the calling thread until a thread terminates, while continuing to
        /// perform standard COM and SendMessage pumping.
        /// </summary>
        /// <exception cref="System.Threading.ThreadInterruptedException">The thread is interrupted while waiting.</exception>
        public virtual void Join()
        {
            if (_baseThread.ThreadState != ThreadState.Unstarted)
                _baseThread.Join();
        }

        /// <summary>
        /// Blocks the calling thread until a thread terminates or the specified time
        /// elapses, while continuing to perform standard COM and SendMessage pumping.
        /// </summary>
        /// <param name="milisecondsTimeout">The number of milliseconds to wait for the thread to terminate.</param>
        /// <returns>true if the thread has terminated; false if the thread has not terminated
        /// after the amount of time specified by the millisecondsTimeout parameter has elapsed.</returns>
        /// <exception cref="System.ArgumentOutOfRangeException">The value of millisecondsTimeout is negative and is not equal to System.Threading.Timeout.Infinite in milliseconds.</exception>
        public virtual bool Join(int milisecondsTimeout)
        {
            if (_baseThread.ThreadState != ThreadState.Unstarted)
                return _baseThread.Join(milisecondsTimeout);
            else
                return true;
        }

        /// <summary>
        /// Blocks the calling thread until a thread terminates or the specified time
        /// elapses, while continuing to perform standard COM and SendMessage pumping.
        /// </summary>
        /// <param name="timeout">
        /// A System.TimeSpan set to the amount of time to wait for the thread to terminate.
        /// </param>
        /// <returns>true if the thread has terminated; false if the thread has not terminated
        /// after the amount of time specified by the timeout parameter has elapsed.</returns>
        /// <exception cref="System.ArgumentOutOfRangeException">
        /// The value of millisecondsTimeout is negative and is not equal to System.Threading.Timeout.Infinite 
        /// in milliseconds, or is greater than System.Int32.MaxValue milliseconds.
        /// </exception>
        public virtual bool Join(TimeSpan timeout)
        {
            if (_baseThread.ThreadState != ThreadState.Unstarted)
                return _baseThread.Join(timeout);
            else
                return true;
        }

        /// <summary>
        /// Sets the ShouldStop property to true an waits indefinitely for the task to end.
        /// </summary>
        public virtual void Stop()
        {
            _ShouldStop = true;
            if (Thread.CurrentThread != _baseThread)
            {
                Join();
            }
        }

        /// <summary>
        /// Sets the ShouldStop property to true an waits for the task to end.
        /// </summary>
        /// <param name="milisecondsTimeout">Time in miliseconds to wait for the task to end.</param>
        /// <returns>True if task ended, false otherwise.</returns>
        public virtual bool Stop(int milisecondsTimeout)
        {
            _ShouldStop = true;
            return Join(milisecondsTimeout);
        }

        /// <summary>
        /// Sets the ShouldStop property to true an waits for the task to end.
        /// </summary>
        /// <param name="timeout">Time to wait for the task to end.</param>
        /// <returns>True if task ended, false otherwise.</returns>
        public virtual bool Stop(TimeSpan timeout)
        {
            _ShouldStop = true;
            return Join(timeout);
        }

        #region Worker Thread


        private EventHandler __EvTaskStarted;
        /// <summary>
        /// Represents the method that will handle the OnJob event.
        /// </summary>
        public event EventHandler TaskStarted
        {
            add
            {
                lock (SyncRoot)
                    __EvTaskStarted += value;
            }
            remove
            {
                lock (SyncRoot)
                    __EvTaskStarted -= value;
            }
        }

        private void OnTaskStarted(EventArgs e)
        {
            EventHandler handler;
            lock (SyncRoot)
            {
                handler = __EvTaskStarted;
            }
            if (handler != null)
                handler(this, e);
        }

        /// <summary>
        /// Task main method. Should return 0 if task completes sucessfully.
        /// </summary>
        protected abstract int Main();


        private EventHandler __EvTaskFinished;
        /// <summary>Fires when the task is finished.</summary>
        public event EventHandler TaskFinished
        {
            add
            {
                lock (SyncRoot)
                    __EvTaskFinished += value;
            }
            remove
            {
                lock (SyncRoot)
                    __EvTaskFinished -= value;
            }
        }

        /// <summary>Represents the method that will handle the TaskFinished event.</summary>
        private void OnTaskFinished(EventArgs e)
        {
            EventHandler handler;
            lock (SyncRoot)
            {
                handler = __EvTaskFinished;
            }
            if (handler != null)
                handler(this, e);
        }


        private EventHandler __EvTaskAbort;
        /// <summary>Fires when the task is aborted.</summary>
        public event EventHandler TaskAbort
        {
            add
            {
                lock (SyncRoot)
                    __EvTaskAbort += value;
            }
            remove
            {
                lock (SyncRoot)
                    __EvTaskAbort -= value;
            }
        }

        /// <summary>Represents the method that will handle the TaskAbort event.</summary>
        private void OnTaskAbort(EventArgs e)
        {
            EventHandler handler;
            lock (SyncRoot)
            {
                handler = __EvTaskAbort;
            }
            if (handler != null)
                handler(this, e);
        }


        private EventHandler<TaskExceptionEventArgs> __EvTaskException;
        /// <summary>Fires when an exception occurs in the task.</summary>
        public event EventHandler<TaskExceptionEventArgs> TaskException
        {
            add
            {
                lock (SyncRoot)
                    __EvTaskException += value;
            }
            remove
            {
                lock (SyncRoot)
                    __EvTaskException -= value;
            }
        }

        /// <summary>Represents the method that will handle the TaskException event.</summary>
        private void OnTaskException(TaskExceptionEventArgs e)
        {
            EventHandler<TaskExceptionEventArgs> handler;
            lock (SyncRoot)
            {
                handler = __EvTaskException;
            }
            if (handler != null)
                handler(this, e);
        }


        private EventHandler __EvTaskTerminate;
        /// <summary>Fires when a task ir termintaing.</summary>
        public event EventHandler TaskTerminate
        {
            add
            {
                lock (SyncRoot)
                    __EvTaskTerminate += value;
            }
            remove
            {
                lock (SyncRoot)
                    __EvTaskTerminate -= value;
            }
        }

        /// <summary>Represents the method that will handle the TaskTerminate event.</summary>
        private void OnTaskTerminate(EventArgs e)
        {
            EventHandler handler;
            lock (SyncRoot)
            {
                handler = __EvTaskTerminate;
            }
            if (handler != null)
                handler(this, e);
        }

        //public event EventHandler TaskStateChanged;


        private EventHandler __EvTaskStateChanged;
        /// <summary>Fires when the task state changes.</summary>
        public event EventHandler TaskStateChanged
        {
            add
            {
                lock (SyncRoot)
                    __EvTaskStateChanged += value;
            }
            remove
            {
                lock (SyncRoot)
                    __EvTaskStateChanged -= value;
            }
        }

        private void ChangeTaskState(TaskState state)
        {
            if (_state != state)
            {
                lock (this.SyncRoot)
                    _state = state;
                EventHandler handler;
                lock (SyncRoot)
                {
                    handler = __EvTaskStateChanged;
                }
                if (handler != null)
                    handler(this, EventArgs.Empty);

            }
        }

        /// <summary>
        /// Threads main function.
        /// </summary>
        private void Worker()
        {
        START:
            //try
            //{
            //    try
            //    {
            lock (this.SyncRoot)
            {
                ChangeTaskState(TaskState.Working);
                OnTaskStarted(EventArgs.Empty);
            }
            if (Main() == 0)
            {
                
            }
            lock (this.SyncRoot)
            {
                ChangeTaskState(TaskState.Finishing);
                OnTaskFinished(EventArgs.Empty);
            }
            //    }
            //    catch (System.Threading.ThreadAbortException)
            //    {
            //        //NOTE: If System.Threading.Thread.ResetAbort(); is not executed this exception will be automatically re-thrown.
            //        System.Threading.Thread.ResetAbort();

            //        lock (this.SyncRoot)
            //        {
            //            ChangeTaskState(TaskState.Aborting);
            //            OnTaskAbort(EventArgs.Empty);
            //        }
            //    }
            //}
            //catch (Exception ex)
            //{
            //    lock (this.SyncRoot)
            //    {
            //        ChangeTaskState(TaskState.HandlingException);
            //        OnTaskException(new TaskExceptionEventArgs(ex));
            //    }
            //}
            //finally
            //{
            //    lock (this.SyncRoot)
            //    {
            //        ChangeTaskState(TaskState.Terminating);
            //        OnTaskTerminate(EventArgs.Empty);
            //    }
            //}
            ChangeTaskState(TaskState.Stopped);
            if (!_ShouldStop && _shouldRestart)
                goto START;
        }

        #endregion

        #endregion
    }


    /// <summary>
    /// When unhandled exception occurs in the Task Worker Thread, the exception will be caught
    /// and an event will be fired with the exception in the arguments.
    /// </summary>
    public class TaskExceptionEventArgs : EventArgs
    {
        /// <summary>
        /// The Exception object.
        /// </summary>
        private Exception _exception;

        /// <summary>
        /// The exception that was catched.
        /// </summary>
        public Exception Ex
        {
            get
            {
                return _exception;
            }
        }

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="exception">Exception object</param>
        public TaskExceptionEventArgs(Exception exception)
        {
            _exception = exception;
        }
    }
}
